﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.IO;

using FCNS.Utility;
using FCNS.IO;
using FCNS.Interfaces;

using FCNS.KeepMoney.Configuration;

namespace FCNS.KeepMoney.Util
{
    /// <summary>
    /// 软件升级
    /// </summary>
   public  class UpdateComponent
    {
        List<UpdateComponentInfo> components;
        string verFile = string.Empty;
        IStatusLogger isl = null;
       public UpdateComponent(List<UpdateComponentInfo> lInst, string VersionUrl,IStatusLogger sl)
        {
            components = lInst;
            verFile = VersionUrl;
            isl = sl;
        }
    
       /// <summary>
        /// 设置升级信息文件的路径（参考：http://www.keepmoney.org/download/version.xml）
        /// </summary>
        public string VersionUrl
        {
            set { verFile = value; }
        }

       /// <summary>
       /// 添加升级组件
       /// </summary>
       /// <param name="lInst"></param>
        public void Add(List<UpdateComponentInfo> lInst)
        {
            components.AddRange(lInst);
        }

       /// <summary>
       /// 检查是否有可用的更新
       /// </summary>
       /// <returns></returns>
        public bool CheckIfUpdateIsAvailThenInstall()
        {
            //if (isl == null)
            //{
            //    Debug.Assert(false, "软件自动升级必须提供状态记录接口 IStatusLogger");
            //    return false;
            //}

            List<string> lUrls = GetUrls();
            Dictionary<string, List<UpdateComponentInfo>> dictAvail = DownloadInfoFiles(lUrls);

            if (dictAvail == null)
                return false;

            MergeInfo(dictAvail);

            bool bUpdAvail = false;
           // System.Windows.Forms.MessageBox.Show(components.Count.ToString());
            foreach (UpdateComponentInfo uc in components)
            {
             //   System.Windows.Forms.MessageBox.Show(uc.VerAvailable.ToString() + "  " + uc.VerInstalled.ToString());

                if (uc.Status == UpdateComponentStatus.NewVerAvailable)
                {
                    bUpdAvail = true;
                   // DownLoadGZipFile(uc);
                    break;
                }
            }

            if (isl != null) { isl.EndLogging(); isl = null; }
            isl = null;

            return bUpdAvail;
        }

        /// <summary>
        /// 检查是否有可用的更新
        /// </summary>
        /// <returns></returns>
        public bool CheckUpdateIsAvail()
        {
            //if (isl == null)
            //{
            //    Debug.Assert(false, "软件自动升级必须提供状态记录接口 IStatusLogger");
            //    return false;
            //}

            List<string> lUrls = GetUrls();
            Dictionary<string, List<UpdateComponentInfo>> dictAvail =
                DownloadInfoFiles(lUrls);
            if (dictAvail == null) return false;
            MergeInfo(dictAvail);

            bool bUpdAvail = false;
            foreach (UpdateComponentInfo uc in components)
            {
                if (uc.Status == UpdateComponentStatus.NewVerAvailable)
                {
                    bUpdAvail = true;
                    break;
                }
            }

            if (isl != null) { isl.EndLogging(); isl = null; }
            isl = null;

            return bUpdAvail;
        }


        private List<string> GetUrls()
        {
            List<string> lUrls = new List<string>();
            foreach (UpdateComponentInfo uc in components)
            {
                string strUrl = uc.UpdateUrl;
                if (string.IsNullOrEmpty(strUrl))
                    continue;

                bool bFound = false;
                for (int i = 0; i < lUrls.Count; ++i)
                {
                    if (lUrls[i].Equals(strUrl, StringComparison.OrdinalIgnoreCase))
                    {
                        bFound = true;
                        break;
                    }
                }

                if (!bFound)
                    lUrls.Add(strUrl);
            }

            return lUrls;
        }

        private  Dictionary<string, List<UpdateComponentInfo>> DownloadInfoFiles(List<string> lUrls)
        {
            List<DownloadInfo> lDl = new List<DownloadInfo>();
            foreach (string strUrl in lUrls)
            {
                if (string.IsNullOrEmpty(strUrl)) { Debug.Assert(false); continue; }

                DownloadInfo dl = new DownloadInfo(strUrl);
                lDl.Add(dl);

                ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadInfoFile), dl);//下载升级信息文件
                //下载更新文件
            }

            while (true)
            {
                bool bReady = true;
                foreach (DownloadInfo dl in lDl)
                {
                    lock (dl.SyncObj) { bReady &= dl.Ready; }
                }

                if (bReady) break;
                Thread.Sleep(40);

                if (isl != null)
                {
                    if (!isl.ContinueWork()) return null;
                }
            }

            Dictionary<string, List<UpdateComponentInfo>> dict =
                new Dictionary<string, List<UpdateComponentInfo>>();
            foreach (DownloadInfo dl in lDl)
            {
                dict[dl.Url.ToLower()] = dl.ComponentInfo;
            }
            return dict;
        }

        private void AddComponent(List<UpdateComponentInfo> l, UpdateComponentInfo c)
        {
            if ((l == null) || (c == null)) { Debug.Assert(false); return; }

            for (int i = l.Count - 1; i >= 0; --i)
            {
                if (l[i].Name.Equals(c.Name, StrUtil.CaseIgnoreCmp))
                    l.RemoveAt(i);
            }

            l.Add(c);
        }

        private  void MergeInfo(Dictionary<string, List<UpdateComponentInfo>> dictAvail)
        {
            string strOvrId = verFile.ToLower();
            List<UpdateComponentInfo> lOvr;
            dictAvail.TryGetValue(strOvrId, out lOvr);

            foreach (UpdateComponentInfo uc in components)
            {
                string strUrlId = uc.UpdateUrl.ToLower();
                List<UpdateComponentInfo> lAvail;
                dictAvail.TryGetValue(strUrlId, out lAvail);

                if (SetComponentAvail(uc, lOvr)) { }
                else if (SetComponentAvail(uc, lAvail)) { }
                else if ((strUrlId.Length > 0) && (lAvail == null))
                    uc.Status = UpdateComponentStatus.DownloadFailed;
                else uc.Status = UpdateComponentStatus.Unknown;
            }
        }

        private  bool SetComponentAvail(UpdateComponentInfo uc,List<UpdateComponentInfo> lAvail)
        {
            if (uc == null) { Debug.Assert(false); return false; }
            if (lAvail == null) return false; // No assert

            foreach (UpdateComponentInfo ucAvail in lAvail)
            {
                //System.Windows.Forms.MessageBox.Show(uc.VerInstalled.ToString() + "  " +
                //    uc.VerAvailable.ToString());
                if (ucAvail.Name.Equals(uc.Name, StrUtil.CaseIgnoreCmp))
                {
                    uc.VerAvailable = ucAvail.VerAvailable;

                    if (uc.VerInstalled == uc.VerAvailable)
                        uc.Status = UpdateComponentStatus.UpToDate;
                    else if (uc.VerInstalled < uc.VerAvailable)
                        uc.Status = UpdateComponentStatus.NewVerAvailable;
                    else uc.Status = UpdateComponentStatus.PreRelease;

                    return true;
                }
            }
            return false;
        }

        private void DownloadInfoFile(object o)
        {
            DownloadInfo dl = (o as DownloadInfo);
            if (dl == null) { Debug.Assert(false); return; }

            dl.ComponentInfo = LoadInfoFile(dl.Url);
            lock (dl.SyncObj) { dl.Ready = true; }
        }

        private List<UpdateComponentInfo> LoadInfoFile(string strUrl)
        {
            try
            {
                IOConnectionInfo ioc = IOConnectionInfo.FromPath(strUrl.Trim());

                Stream s = IOConnection.OpenRead(ioc);
                if (s == null) throw new InvalidOperationException();
                MemoryStream ms = new MemoryStream();
                MemUtil.CopyStream(s, ms);
                s.Close();
                byte[] pb = ms.ToArray();
                ms.Close();

                if (ioc.Path.EndsWith(".gz", StrUtil.CaseIgnoreCmp))
                    pb = MemUtil.Decompress(pb);

                string strData = StrUtil.Utf8.GetString(pb);
                strData = StrUtil.NormalizeNewLines(strData, false);
                string[] vLines = strData.Split('\n');

                List<UpdateComponentInfo> l = new List<UpdateComponentInfo>();
                bool bHeader = true, bFooterFound = false;
                char chSep = ':'; // Modified by header
                for (int i = 0; i < vLines.Length; ++i)
                {
                    string str = vLines[i].Trim();
                    if (str.Length == 0) continue;

                    if (bHeader)
                    {
                        chSep = str[0];
                        bHeader = false;
                    }
                    else if (str[0] == chSep)
                    {
                        bFooterFound = true;
                        break;
                    }
                    else // Component info
                    {
                        string[] vInfo = str.Split(new char[] { chSep });
                        if (vInfo.Length >= 2)
                        {
                            UpdateComponentInfo c = new UpdateComponentInfo(
                                vInfo[0].Trim(), 0, ioc.Path, string.Empty);
                            c.VerAvailable = StrUtil.GetVersion(vInfo[1]);

                            AddComponent(l, c);
                        }
                    }
                }

                return (bFooterFound ? l : null);
            }
            catch (Exception) { }

            return null;
        }

        private bool DownLoadGZipFile(UpdateComponentInfo componentInfo)
        {
            string str = (Path.GetDirectoryName(componentInfo.UpdateUrl) + "\\"
                + componentInfo.Name + ".gz").Replace('\\', '/');

            if (!str.StartsWith("http://"))
                str = str.Replace("http:/", "http://");

            Stream s = null;
            try
            {
                IOConnectionInfo ioc = IOConnectionInfo.FromPath(str);
                s = IOConnection.OpenRead(ioc);
            }
            catch
            {
                componentInfo.Status = UpdateComponentStatus.DownloadFailed;
                if (s == null)
                    throw new InvalidOperationException();

                return false;
            }

            MemoryStream ms = new MemoryStream();
            MemUtil.CopyStream(s, ms);
            FileStream file = new FileStream(AppDefines.appPath + "update\\" + componentInfo.Name + ".gz", FileMode.Create);
            file.Write(ms.ToArray(), 0, (int)ms.Length);
            file.Flush();
            s.Close();
            file.Close();

            return true;
        }

        private bool GZipFile()
        {
            return true;
        }
    }
}
